package yxy.banana.socket.coder;

import java.net.URI;
import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import yxy.apple.nio.Coder;
import yxy.apple.protobuf.Message.Parcal;

public abstract class WebSocketProtobufCoder extends Coder {
	public static final int MaxFrameLength = 0x0000FFFF;// 64k

	public static enum Part {
		Server, Client
	}

	private String subprotocol = null;
	private Part part = Part.Server;

	public WebSocketProtobufCoder(String subprotocol, Part part) {
		this.subprotocol = subprotocol;
		this.part = part;
	}

	/**
	 * 仅Part.Server时回调
	 */
	protected abstract String resource(ChannelPipeline pipeline);

	/**
	 * 仅Part.Client是回调
	 * 
	 * @param pipeline
	 * @return
	 */
	protected abstract URI uri(ChannelPipeline pipeline);

	/**
	 * 是否允许扩展，子类可按需覆盖，默认为true
	 * 
	 * @return
	 */
	protected boolean extensions(ChannelPipeline pipeline) {
		return true;
	}

	@Override
	public void assemble(ChannelPipeline pipeline) {
		boolean extensions = this.extensions(pipeline);
		if (this.part == Part.Server) {
			pipeline.addLast(new HttpServerCodec());
		} else {
			pipeline.addLast(new HttpClientCodec());
		}
		pipeline.addLast(new HttpObjectAggregator(WebSocketProtobufCoder.MaxFrameLength));
		pipeline.addLast(new ChunkedWriteHandler());
		if (this.part == Part.Server) {
			pipeline.addLast(new WebSocketServerCompressionHandler());
			pipeline.addLast(new WebSocketServerProtocolHandler(this.resource(pipeline), this.subprotocol, extensions));
		} else {
			pipeline.addLast(WebSocketClientCompressionHandler.INSTANCE);
			pipeline.addLast(new WebSocketClientProtocolHandler(this.uri(pipeline), WebSocketVersion.V08,
					this.subprotocol, extensions, null, WebSocketProtobufCoder.MaxFrameLength));
		}

		//decoder
		pipeline.addLast(new MessageToMessageDecoder<BinaryWebSocketFrame>() {
			@Override
			protected void decode(ChannelHandlerContext ctx, BinaryWebSocketFrame frame, List<Object> out)
					throws Exception {
				ByteBuf buffer = frame.content();
				out.add(buffer);
				buffer.retain();//谁给他release？
			}
		});
		pipeline.addLast(new ProtobufDecoder(Parcal.getDefaultInstance()));

		pipeline.addLast(new MessageToMessageEncoder<ByteBuf>() {
			@Override
			protected void encode(ChannelHandlerContext context, ByteBuf buffer, List<Object> output) throws Exception {
				BinaryWebSocketFrame frame = new BinaryWebSocketFrame(buffer);
				output.add(frame);
				buffer.retain();//谁给他release？
			}
		});
		pipeline.addLast(new ProtobufEncoder());
	}

	public String subprotocol() {
		return this.subprotocol;
	}

}
